
#' The Assay Class
#'
#' The Assay object is the basic unit of Seurat; each Assay stores raw,
#' normalized, and scaled data as well as cluster information, variable
#' features, and any other assay-specific metadata. Assays should contain single
#' cell expression data such as RNA-seq, protein, or imputed expression data.
#'
#' @slot counts Unnormalized data such as raw counts or TPMs
#' @slot data Normalized expression data
#' @slot scale.data Scaled expression data
#' @slot key Key for the Assay
#' @slot assay.orig Original assay that this assay is based off of. Used to
#' track assay provenance
#' @slot var.features Vector of features exhibiting high variance across
#' single cells
#' @slot meta.features Feature-level metadata
#' @slot misc Utility slot for storing additional data associated with the assay
#'
#' @name Assay-class
#' @rdname Assay-class
#' @exportClass Assay
#'
#' @concept assay
#'
#' @seealso \code{\link{Assay-methods}}
#'
Assay <- setClass(
  Class = 'Assay',
  slots = c(
    counts = 'AnyMatrix',
    data = 'AnyMatrix',
    scale.data = 'matrix',
    key = 'character',
    assay.orig = 'OptionalCharacter',
    var.features = 'vector',
    meta.features = 'data.frame',
    misc = 'OptionalList'
  )
)


# AnyMatrix 定义在 seurat-object-4.0.4/R/zzz.R:41
setClassUnion(name = 'AnyMatrix', members = c("matrix", "dgCMatrix"))
setClassUnion(name = 'OptionalCharacter', members = c('NULL', 'character'))
setClassUnion(name = 'OptionalList', members = c('NULL', 'list'))







#' Create an Assay object
#'
#' Create an Assay object from a feature (e.g. gene) expression matrix. The
#' expected format of the input matrix is features x cells.
#'
#' Non-unique cell or feature names are not allowed. Please make unique before
#' calling this function.
#'
#' @param counts Unnormalized data such as raw counts or TPMs
#' @param data Prenormalized data; if provided, do not pass \code{counts}
#' @param min.cells Include features detected in at least this many cells. Will
#' subset the counts matrix as well. To reintroduce excluded features, create a
#' new object with a lower cutoff.
#' @param min.features Include cells where at least this many features are
#' detected.
#' @param check.matrix Check counts matrix for NA, NaN, Inf, and non-integer values
#' @param ... Arguments passed to \code{\link{as.sparse}}
#'
#' @return A \code{\link{Assay}} object
#'
#' @importFrom methods as
#' @importFrom Matrix colSums rowSums
#'
#' @export
#'
#' @concept assay
#'
#' @examples
#' \dontrun{
#' pbmc_raw <- read.table(
#'   file = system.file('extdata', 'pbmc_raw.txt', package = 'Seurat'),
#'   as.is = TRUE
#' )
#' pbmc_rna <- CreateAssayObject(counts = pbmc_raw)
#' pbmc_rna
#' }
#'
CreateAssayObject <- function(
  counts,
  data,
  min.cells = 0,
  min.features = 0,
  check.matrix = FALSE,
  ...
) {
  # counts 和 data 都缺，报错
  if (missing(x = counts) && missing(x = data)) {
    stop("Must provide either 'counts' or 'data'")
  # 如果都提供，也报错
  } else if (!missing(x = counts) && !missing(x = data)) {
    stop("Either 'counts' or 'data' must be missing; both cannot be provided")

  #如果只提供 counts 参数
  } else if (!missing(x = counts)) {

    # check that dimnames of input counts are unique
    if (anyDuplicated(x = rownames(x = counts))) { #返回重复的元素下标，没有则返回0
      warning(
        "Non-unique features (rownames) present in the input matrix, making unique",
        call. = FALSE,
        immediate. = TRUE
      )
      rownames(x = counts) <- make.unique(names = rownames(x = counts)) #通过添加.1后缀强制uniq
    }

    
    # 同上：强制列名uniq
    if (anyDuplicated(x = colnames(x = counts))) {
      warning(
        "Non-unique cell names (colnames) present in the input matrix, making unique",
        call. = FALSE,
        immediate. = TRUE
      )
      colnames(x = counts) <- make.unique(names = colnames(x = counts))
    }

    # 如果没有列名，报错
    if (is.null(x = colnames(x = counts))) {
      stop("No cell names (colnames) names present in the input matrix")
    }

    # 如果任何一个基因名为""，报错
    if (any(rownames(x = counts) == '')) {
      stop("Feature names of counts matrix cannot be empty", call. = FALSE)
    }

    # 如果矩阵行数>0，但是行名为空，报错
    if (nrow(x = counts) > 0 && is.null(x = rownames(x = counts))) {
      stop("No feature names (rownames) names present in the input matrix")
    }

    # 如果输入的counts不是稀疏矩阵
    if (!inherits(x = counts, what = 'dgCMatrix')) {
      # 如果是df，则转为稀疏矩阵，使用...输入参数
      if (inherits(x = counts, what = "data.frame")) {
        counts <- as.sparse(x = counts, ...)
      # 否则仅仅转为稀疏矩阵
      } else {
        counts <- as.sparse(x = counts)
      }
    }

    # 这是啥？ 原来是一个传入参数 check.matrix=F，默认跳过
    if (isTRUE(x = check.matrix)) {
      # 如果不跳过，这一步到底做了什么？ 函数定义在 utils.R
      # 检查是否有异常值：无穷大，逻辑值，非整数，NA，如果有就警告；没返回值。
      CheckMatrix(object = counts)
    }


    ##################
    # 开始过滤
    # Filter based on min.features
    if (min.features > 0) {
      nfeatures <- Matrix::colSums(x = counts > 0) #计算每列的>0和(基因数 per cell)
      counts <- counts[, which(x = nfeatures >= min.features)] #过滤掉基因数太少的细胞
    }

    # filter genes on the number of cells expressing
    if (min.cells > 0) {
      num.cells <- Matrix::rowSums(x = counts > 0) #计算每行>0的和(表达该基因的细胞数)
      counts <- counts[which(x = num.cells >= min.cells), ] #过滤掉表达的细胞数太少的基因
    }

    # 赋值给 data
    data <- counts

  # 如果传入参数只有一个 data 呢？
  } else if (!missing(x = data)) {
    # check that dimnames of input data are unique
    if (anyDuplicated(x = rownames(x = data))) { #行名是否重复(gene)
      warning(
        "Non-unique features (rownames) present in the input matrix, making unique",
        call. = FALSE,
        immediate. = TRUE
      )
      rownames(x = data) <- make.unique(names = rownames(x = data)) #行名强制uniq
    }

    #列名(cid) 强制uniq
    if (anyDuplicated(x = colnames(x = data))) {
      warning(
        "Non-unique cell names (colnames) present in the input matrix, making unique",
        call. = FALSE,
        immediate. = TRUE
      )
      colnames(x = data) <- make.unique(names = colnames(x = data))
    }

    # cid不能为空
    if (is.null(x = colnames(x = data))) {
      stop("No cell names (colnames) names present in the input matrix")
    }
    # symbol 不能有""
    if (any(rownames(x = data) == '')) {
      stop("Feature names of data matrix cannot be empty", call. = FALSE)
    }

    #行数>0 且 行名为null，报错
    if (nrow(x = data) > 0 && is.null(x = rownames(x = data))) {
      stop("No feature names (rownames) names present in the input matrix")
    }

    # 如果传入的是 data 则不过滤，只过滤 counts 参数。
    if (min.cells != 0 | min.features != 0) {
      warning(
        "No filtering performed if passing to data rather than counts",
        call. = FALSE,
        immediate. = TRUE
      )
    }
    # 给counts 赋值一个0x0空矩阵
    counts <- new(Class = 'matrix')
  }


  # Ensure row- and column-names are vectors, not arrays
  # 区别是啥？向量是单列的，数组是2维的
  if (!is.vector(x = rownames(x = counts))) {
    rownames(x = counts) <- as.vector(x = rownames(x = counts))
  }
  if (!is.vector(x = colnames(x = counts))) {
    colnames(x = counts) <- as.vector(x = colnames(x = counts))
  }
  if (!is.vector(x = rownames(x = data))) {
    rownames(x = data) <- as.vector(x = rownames(x = data))
  }
  if (!is.vector(x = colnames(x = data))) {
    colnames(x = data) <- as.vector(x = colnames(x = data))
  }


  # 行名(gene symbol) 不能包含下划线_，替换成减号-
  if (any(grepl(pattern = '_', x = rownames(x = counts))) || any(grepl(pattern = '_', x = rownames(x = data)))) {
    warning(
      "Feature names cannot have underscores ('_'), replacing with dashes ('-')",
      call. = FALSE,
      immediate. = TRUE
    )

    rownames(x = counts) <- gsub( #全局替换
      pattern = '_',
      replacement = '-',
      x = rownames(x = counts)
    )
    rownames(x = data) <- gsub(
      pattern = '_',
      replacement = '-',
      x = rownames(x = data)
    )
  }

  # 行名(gene symbol) 不能包含管道符|，替换成减号-
  if (any(grepl(pattern = '|', x = rownames(x = counts), fixed = TRUE)) || any(grepl(pattern = '|', x = rownames(x = data), fixed = TRUE))) {
    warning(
      "Feature names cannot have pipe characters ('|'), replacing with dashes ('-')",
      call. = FALSE,
      immediate. = TRUE
    )
    rownames(x = counts) <- gsub(
      pattern = '|',
      replacement = '-',
      x = rownames(x = counts),
      fixed = TRUE
    )
    rownames(x = data) <- gsub(
      pattern = '|',
      replacement = '-',
      x = rownames(x = data),
      fixed = TRUE
    )
  }


  # Initialize meta.features
  init.meta.features <- data.frame(row.names = rownames(x = data)) #空df，只有行名(gene symbol)
  # Seurat 对象slot = meta.data 行名是 cell id； 
  # 这个Assay的slot = meta.features, 行名是 symbol: colnames(x = data)

  ###############
  # 实例化一个 Assay对象，并返回
  assay <- new(
    Class = 'Assay',
    counts = counts, # 如果仅传入 counts，则要过滤，最后 data=counts
    data = data, # 如果仅传入 data, 则不过滤，最后 counts=空矩阵
    scale.data = new(Class = 'matrix'), #空矩阵
    meta.features = init.meta.features, #空df，行名为 gene symbol
    misc = list() #空list
  )
  return(assay)
}
